1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.api.data.tilemap;
12 public import hip.api.renderer.texture;
13 
14 /**
15 *   TileLayers representations which can be found from Tiled.
16 */
17 enum TileLayerType
18 {
19     ///The Default one, a data structure holding tile mapping information.
20     TILE_LAYER   = "tilelayer",
21     ///A simple layer, containing only an image to be rendered above or below the tile layers.
22     IMAGE_LAYER  = "imagelayer",
23     ///A layer used only to hold data to be used within the gameplay implementation
24     OBJECT_LAYER = "objectgroup"
25 }
26 
27 enum TileDrawOrder
28 {
29     TOP_DOWN,
30     DOWN_TOP
31 }
32 
33 /**
34 *   Simple Key/Value type defining a property inside a Tile.
35 */
36 struct TileProperty
37 {
38     string name;
39     string type;
40     string value;
41     string set(string v){return value = v;}
42     string toString() const => value;
43 
44 
45     version(Have_util)
46     {
47         import hip.util.sumtype;
48         Sumtype val;
49         pragma(inline, true) T get(T)()
50         {
51             if(val.type == Type.undefined)
52                 val = Sumtype.make!T(value);
53             return val.get!T;
54         }
55         pragma(inline, true) T set(T)(T v) => val.set(v);
56     }
57 }
58 
59 /**
60 *   A simple object which can mean absolutely anything inside a TileLayer.
61 *   Usually used for implementing:
62 -   Camera Dead Zones
63 -   Event Systems
64 -   Trigger Areas
65 */
66 struct TileLayerObject
67 {
68     ushort gid;
69     uint height;
70     ushort id;
71     string name;
72     int rotation;
73     string type;
74     bool visible;
75     uint width;
76     int x;
77     int y;
78     TileProperty[string] properties;
79 }
80 
81 /**
82 *   The TileAnimationFrame holds what is the ID of the current frame in a TileAnimation and how much duration.
83 */
84 struct TileAnimationFrame
85 {
86     ushort id;
87     int duration;
88 }
89 
90 /**
91 *   The Tile is the smallest piece of a TileMap.
92 */
93 struct Tile
94 {
95     ///ID for backreferencing from TileMap
96     ushort id;
97     ///Which frame is in its animatoin
98     ushort currentFrame;
99     ///The tile texture region itself.
100     IHipTextureRegion region;
101     /**
102     *   Properties about the tile. Usually used for implementing gameplay stats such as:
103     -   Sounds
104     -   Collisions and Collidibles
105     -   Visual Effects
106     */
107     TileProperty[string] properties;
108     ///Frames for playing the tile animation
109     TileAnimationFrame[] animation;
110     alias properties this;
111 }
112 
113 /**
114 *   Tile Layer is a map of tiles consisting in a unique "texture" of tiles.
115 */
116 final class HipTileLayer
117 {
118     ///Layer name on the tilemap editor
119     string name;
120     ///Data
121     ushort[] tiles;
122     bool visible = true;
123     int x, y, columns, rows, width, height;
124     uint tileWidth, tileHeight;
125     ushort id;
126     string type;
127     string drawOrder;
128     TileProperty[string] properties;
129     float opacity = 1.0;
130 
131     this(IHipTilemap map)
132     {
133         tileWidth = map.tileWidth;
134         tileHeight = map.tileHeight;
135     }
136 
137     /**
138     *
139     */
140     this(string name, uint columns, uint rows, ushort id, IHipTilemap map)
141     {
142         this(map);
143         this.name = name;
144         this.id = id;
145         this.columns = columns;
146         this.rows = rows;
147         tiles = new ushort[columns*rows];
148         width = columns*tileWidth;
149         height = rows*tileHeight;
150     }
151 
152     bool isInLayerBoundaries(int x, int y, int w, int h) const @nogc
153     {
154         int x2 = x+w;
155         int y2 = y+h;
156 
157         bool notInBoundariesX = x2 < this.x || x > this.width + this.x;
158         if(notInBoundariesX)
159             return false;
160         bool notInBoundariesY = y2 < this.y || y > this.height + this.y;
161 
162         return !notInBoundariesY;
163     }
164 
165     ///Expects I and J in column/row
166     final ushort getTile(uint i, uint j) @nogc @safe
167     {
168        return tiles[j*columns+i];
169     }
170     final ushort checkedGetTile(uint i, uint j) @nogc @trusted
171     {
172         int target = j*columns+i;
173         if(i >= columns || j >= rows || target < 0 || target >= tiles.length)
174             return 0;
175         return tiles.ptr[target];
176     }
177 
178     ///Gets tile from relative X and Y. Does not take into account the layer x, y
179     final ushort getTileXY(uint x, uint y) @nogc @safe
180     {
181         return getTile(cast(uint)(x / tileWidth), cast(uint)(y / tileHeight));
182     }
183 
184     ///Gets tile from absolute X and Y. Takes into account the layer x, y
185     final ushort checkedGetTileXY(int x, int y) @nogc @trusted
186     {
187         if(x < this.x || y < this.y || x > this.x+this.width || y > this.y+this.height)
188             return 0;
189         y-= this.y;
190         x-= this.x;
191         return checkedGetTile(x / tileWidth, y / tileHeight);
192     }
193     
194 }
195 
196 /**
197 *   Tileset is a data set containing tiles. It has information about the underlying texture used for a tile.
198 */
199 interface IHipTileset
200 {
201     uint columns() const;
202 
203     ///Means where the tileset id starts
204     uint firstGid() const;
205     
206 
207     ///"image" in tiled
208 
209     string texturePath() const;
210     ///"imageheight" in tiled
211     uint  textureHeight() const;
212     ///"imagewidth" in tiled 
213     uint  textureWidth() const; 
214 
215     IHipTexture texture();
216     int margin() const;
217     string name() const;
218 
219     ///Only available when loaded via .tsx
220     string path() const;
221     int spacing() const;
222     uint tileHeight() const;
223     uint tileWidth() const;
224 
225     ///Usually only accessed when looking for a specific property
226     Tile[] tiles();
227 
228     final uint tileCount()const {return cast(uint)(cast(IHipTileset)this).tiles.length;}
229     final IHipTextureRegion getTextureRegion(ushort id)
230     {
231         return tiles[id - firstGid].region;
232     }
233     final Tile* getTile(ushort id){return &tiles[id - firstGid];}
234 }
235 
236 
237 /**
238 *   Tilemap is a set of tile layers. It contains information on the maximum map size, holds all the tilesets needed for
239 *   actually drawing the layers.
240 */
241 interface IHipTilemap
242 {
243     @nogc ref int x();
244     @nogc ref int y();
245     @nogc ref HipColor color();
246     @nogc ref float scaleX();
247     @nogc ref float scaleY();
248 
249     ///Returns scaleX as the one to be modified.
250     @nogc float scale();
251     ///Modifies both scaleX and scaleY at the same time.
252     @nogc float scale(float v);
253     ref float rotation();
254     
255 
256     string path() const;
257     uint width() const @nogc;
258     uint height() const @nogc;
259     bool isInfinite() const @nogc;
260     ref HipTileLayer[string] layers();
261     string orientation() const @nogc;
262     string renderorder() const @nogc;
263     string tiled_version() const @nogc;
264 
265     uint tileHeight() const @nogc;
266     uint tileWidth() const @nogc;
267 
268     final uint tileWidthScaled() @nogc {return cast(uint)(scaleX * tileWidth);}
269     final uint tileHeightScaled() @nogc {return cast(uint)(scaleY * tileHeight);}
270 
271     void setTileSize(uint tileWidth, uint tileHeight);
272     ///Use it when programatically creating your tilemap
273     void addTileset(IHipTileset tileset);
274     ///Use it when programatically creating your tilemap
275     final HipTileLayer addNewLayer(string layerName, uint columns, uint rows)
276     {
277         return layers[layerName] = new HipTileLayer(layerName, columns, rows, cast(ushort)layers.length, this);
278     }
279     
280 
281     IHipTileset getTilesetForID(ushort id);
282     final IHipTextureRegion getTextureRegionForID(ushort id){return getTilesetForID(id).getTextureRegion(id);}
283     final Tile* getTileForID(ushort id){return getTilesetForID(id).getTile(id);}
284 
285 
286     alias layers this;
287 }